import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;

import com.ebocy.hammer.core.log.EbocyLogManager;
import com.ebocy.hammer.core.log.LoggerConstants.CuteLoggerType;

public class NIOServer {
	Logger logger1 = EbocyLogManager.getLogger(CuteLoggerType.RECORD1, Level.INFO);
	Logger logger2 = EbocyLogManager.getLogger(CuteLoggerType.RECORD2, Level.INFO);
	Logger logger3 = EbocyLogManager.getLogger(CuteLoggerType.RECORD3, Level.INFO);

	public static final int PORT = 12315;

	protected Selector selector;
	protected Charset charset = Charset.forName("UTF-8");
	protected CharsetEncoder charsetEncoder = charset.newEncoder();
	protected CharsetDecoder charsetDecoder = charset.newDecoder();

	int clientCount;

	public NIOServer() throws Exception {
		selector = Selector.open();
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.socket().bind(new InetSocketAddress(PORT)); // port
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// register
		println("Server localhost:" + PORT + " started. waiting for clients. ");
		while (true) {
			// selector 线程。select() 会阻塞，直到有客户端连接，或者有消息读入
			selector.select();
			Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
			while (iterator.hasNext()) {
				SelectionKey selectionKey = iterator.next();
				iterator.remove(); // 删除此消息
				// 并在当前线程内处理。（为了高效，一般会在另一个线程中处理此消息，例如使用线程池等）
				handleSelectionKey(selectionKey);
			}
		}

	}

	public void handleSelectionKey(SelectionKey selectionKey) throws Exception {
		if (selectionKey.isAcceptable()) {
			// 有客户端进来
			clientCount++;
			System.out.println(clientCount);

			ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
			SocketChannel socketChannel = serverSocketChannel.accept();
			socketChannel.configureBlocking(false);
			Socket socket = socketChannel.socket();

			// 立即注册一个 OP_READ 的SelectionKey, 接收客户端的消息
			SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ);
			key.attach("第 " + clientCount + " 个客户端 [" + socket.getRemoteSocketAddress() + "]: ");

			println(key.attachment() + "\t[connected] =========================================");

		} else if (selectionKey.isReadable()) {
			// 有消息进来
			ByteBuffer byteBuffer = ByteBuffer.allocate(100);
			SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
			try {
				int len = socketChannel.read(byteBuffer);
				String address = socketChannel.socket().getRemoteSocketAddress().toString();
				int flag = Integer.parseInt(address.substring(address.indexOf("16.") + 3, address.indexOf(":")));

				// 如果len>0，表示有输入。如果len==0, 表示输入结束。需要关闭 socketChannel
				if (len > 0) {
					byteBuffer.flip();
					printBuffer(flag, byteBuffer);
				} else {
					// 输入结束，关闭 socketChannel
					println(selectionKey.attachment() + "read finished. close socketChannel. ");
					socketChannel.close();
				}

			} catch (Exception e) {

				// 如果read抛出异常，表示连接异常中断，需要关闭 socketChannel
				e.printStackTrace();

				println(selectionKey.attachment() + "socket closed? ");
				socketChannel.close();
			}

		} else if (selectionKey.isWritable()) {
			println(selectionKey.attachment() + "TODO: isWritable() ???????????????????????????? ");
		} else if (selectionKey.isConnectable()) {
			println(selectionKey.attachment() + "TODO: isConnectable() ????????????????????????? ");
		} else {
			println(selectionKey.attachment() + "TODO: else. ");
		}

	}

	public static void println(Object object) {
		// System.out.println(object);
	}

	private void printBuffer(int count, ByteBuffer oneTimesBuffer) {
		if (oneTimesBuffer == null)
			return;
		byte[] bytes = new byte[oneTimesBuffer.limit()];
		oneTimesBuffer.get(bytes);
		oneTimesBuffer.flip();
		switch (count) {
		case 111:
			logger1.info(IoBuffer.wrap(bytes).getHexDump());
			break;
		case 166:
			logger2.info(IoBuffer.wrap(bytes).getHexDump());
			break;
		case 218:
			logger3.info(IoBuffer.wrap(bytes).getHexDump());
			break;
		}

	}

	public static void main(String[] args) throws Exception {
		new NIOServer();
	}

}